﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using StripeOne.Core.S1Exceptions;
using StripeOne.Core.S1Config;
using StripeOne.Core.S1Tools;
using System.Web;
using System.Diagnostics;

namespace StripeOne.Core.S1Log
{
    public class S1Log
    {
        private S1LogTypeConfig verboseConfig;
		private S1LogTypeConfig logConfig;
		private S1LogTypeConfig errorLogConfig;
		private S1LogTypeConfig exceptionConfig;
		private S1LogConfig _config;
		private S1LogDelegate _customLogger;
		private string _identifier;
		private static S1LogDelegate _defaultCustomLogger;
		private static S1LogDelegate _redirectLogger;
		public static S1Log Default
		{
			get
			{
				return new S1Log();
			}
		}
		public S1Log() : this(string.Empty, S1Log._defaultCustomLogger)
		{
		}
		public S1Log(S1LogDelegate customLogger) : this(string.Empty, customLogger)
		{
		}
		public S1Log(string identifier) : this(identifier, S1Log._defaultCustomLogger)
		{
		}
		public S1Log(string identifier, S1LogDelegate customLogger)
		{
			this._config = new S1LogConfig();
			this._identifier = identifier;
			this._customLogger = customLogger;
			this.verboseConfig = new S1LogTypeConfig("verbose");
			this.logConfig = new S1LogTypeConfig("log");
			this.errorLogConfig = new S1LogTypeConfig("errorLog");
			this.exceptionConfig = new S1LogTypeConfig("exception");
		}
		public void WriteLog(string message, S1LogLevel level)
		{
			S1LogTypeConfig SoLogTypeConfig;
			switch (level)
			{
			case S1LogLevel.Verbose:
				SoLogTypeConfig = this.verboseConfig;
				break;

			case S1LogLevel.Log:
				SoLogTypeConfig = this.logConfig;
				break;

			case S1LogLevel.Error:
				SoLogTypeConfig = this.errorLogConfig;
				break;

			case S1LogLevel.Exception:
				SoLogTypeConfig = this.exceptionConfig;
				break;

			default:
				throw new S1LogException("Invalid SoLogLevel specified", null);
			}
			if (S1Log._redirectLogger != null)
			{
				if (SoLogTypeConfig.UseRedirect)
				{
					S1Log._redirectLogger(message, this._identifier, level);
				}
				return;
			}
			if (SoLogTypeConfig.UseS1Trace || SoLogTypeConfig.UseTrace || SoLogTypeConfig.UseFile || SoLogTypeConfig.UseEventViewer || SoLogTypeConfig.UseCustom)
			{
				string str = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - ";
				string str2 = string.Empty;
				if (this._identifier != null)
				{
					str2 = ", Identifier: " + this._identifier.ToString();
				}
				if (SoLogTypeConfig.UseS1Trace && HttpContext.Current != null)
				{
					object obj = HttpContext.Current.Items["S1TRACE_DATA"];
					List<S1LogItem> list;
					if (obj != null && obj is List<S1LogItem>)
					{
						list = (List<S1LogItem>)obj;
					}
					else
					{
						list = new List<S1LogItem>();
						HttpContext.Current.Items["S1TRACE_DATA"] = list;
					}
					list.Add(new S1LogItem
					{
						Message = message,
						Identifier = this._identifier,
						Level = level,
						Timestamp = S1Timestamp.GetTimestamp()
					});
				}
				if (SoLogTypeConfig.UseTrace && HttpContext.Current != null)
				{
					if (level == S1LogLevel.Error || level == S1LogLevel.Exception)
					{
						HttpContext.Current.Trace.Warn(message);
					}
					else
					{
						HttpContext.Current.Trace.Write(message);
					}
				}
				if (SoLogTypeConfig.UseFile)
				{
					if (this._config.FileLogPath.Length > 0 && this._config.FileLogTag.Length > 0)
					{
						if (!Directory.Exists(this._config.FileLogPath))
						{
							try
							{
								Directory.CreateDirectory(this._config.FileLogPath);
							}
							catch (Exception innerException)
							{
								throw new S1ConfigException("Cannot create directory \"" + this._config.FileLogPath + "\" for logging", innerException);
							}
						}
						FileStream fileStream = null;
						try
						{
							try
							{
								string text;
								if (!string.IsNullOrEmpty(this._identifier))
								{
									text = string.Concat(new string[]
									{
										this._config.FileLogTag,
										"_",
										this._identifier,
										"_",
										level.ToString(),
										"_",
										DateTime.Now.ToString("yyyyMMdd")
									});
								}
								else
								{
									text = string.Concat(new string[]
									{
										this._config.FileLogTag,
										"_",
										level.ToString(),
										"_",
										DateTime.Now.ToString("yyyyMMdd")
									});
								}
								string text2 = text + ".log";
								if (this._config.FileLogMaxLength > 0)
								{
									List<string> list2 = new List<string>(Directory.GetFiles(this._config.FileLogPath, text + "[*].log"));
									if (list2 != null && list2.Count > 0)
									{
										list2.Sort(delegate(string a, string b)
										{
											int num3 = a.LastIndexOf("[") + 1;
											int num4 = a.Substring(num3, a.LastIndexOf("]") - num3).ToInt32() + 1;
											num3 = b.LastIndexOf("[") + 1;
											int value = b.Substring(num3, b.LastIndexOf("]") - num3).ToInt32() + 1;
											return num4.CompareTo(value);
										}
										);
										text2 = list2[list2.Count - 1];
										fileStream = File.Open(Path.Combine(this._config.FileLogPath, text2), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
										if (fileStream.Length > (long)this._config.FileLogMaxLength)
										{
											fileStream.Close();
											int num = text2.LastIndexOf("[") + 1;
											int num2 = text2.Substring(num, text2.LastIndexOf("]") - num).ToInt32() + 1;
											text2 = string.Concat(new object[]
											{
												text,
												"[",
												num2,
												"].log"
											});
											fileStream = File.Open(Path.Combine(this._config.FileLogPath, text2), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
										}
									}
									else
									{
										fileStream = File.Open(Path.Combine(this._config.FileLogPath, text2), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
										if (fileStream.Length > (long)this._config.FileLogMaxLength)
										{
											fileStream.Close();
											fileStream = File.Open(Path.Combine(this._config.FileLogPath, text + "[1].log"), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
										}
									}
								}
								else
								{
									fileStream = File.Open(Path.Combine(this._config.FileLogPath, text2), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
								}
								fileStream.Seek(0L, SeekOrigin.End);
								byte[] bytes = Encoding.Default.GetBytes(str + message + Environment.NewLine);
								fileStream.Write(bytes, 0, bytes.Length);
							}
							catch (Exception innerException2)
							{
								throw new S1ConfigException("Error logging information to file log", innerException2);
							}
						}
						finally
						{
							if (fileStream != null)
							{
								fileStream.Close();
							}
						}
					}
					throw new S1ConfigException("Cannot use file logging if the \"fileLogFolder\" and \"fileLogTag\" settings are not present", null);
				}
				if (SoLogTypeConfig.UseEventViewer)
				{
					if (this._config.EventViewerLog.Length > 0 && this._config.EventViewerSource.Length > 0)
					{
						EventLog eventLog = null;
						try
						{
							try
							{
								eventLog = new EventLog();
								eventLog.Log = this._config.EventViewerLog;
								eventLog.Source = this._config.EventViewerSource;
								if (level == S1LogLevel.Error || level == S1LogLevel.Exception)
								{
									eventLog.WriteEntry(message + str2, EventLogEntryType.Error);
								}
								else
								{
									eventLog.WriteEntry(message + str2, EventLogEntryType.Information);
								}
							}
							catch (Exception innerException4)
							{
								throw new S1LogException("Error logging information to Event Viewer", innerException4);
							}
							goto IL_6D1;
						}
						finally
						{
							if (eventLog != null)
							{
								eventLog.Close();
							}
						}
					}
					throw new S1ConfigException("Cannot use event viewer logging if the \"eventViewerLog\" and \"eventViewerSource\" settings are not present", null);
				}
				IL_6D1:
				if (SoLogTypeConfig.UseCustom && this._customLogger != null)
				{
					this._customLogger(message, this._identifier, level);
				}
			}
		}
		public void Verbose(object obj)
		{
			if (this.verboseConfig.UseTrace || this.verboseConfig.UseFile || this.verboseConfig.UseEventViewer || this.verboseConfig.UseCustom)
			{
				StringBuilder stringBuilder = new StringBuilder();
				string[] propertyNames = S1Reflection.GetPropertyNames(obj);
				string[] fieldNames = S1Reflection.GetFieldNames(obj);
				string[] array = propertyNames;
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i];
					stringBuilder.Append("(Property) " + text + ": ");
					object propertyValue = S1Reflection.GetPropertyValue(obj, text);
					if (propertyValue == null)
					{
						stringBuilder.Append("null");
					}
					else
					{
						stringBuilder.Append(propertyValue.ToString());
					}
					stringBuilder.Append(Environment.NewLine);
				}
				string[] array2 = fieldNames;
				for (int j = 0; j < array2.Length; j++)
				{
					string text2 = array2[j];
					stringBuilder.Append("(Field) " + text2 + ": ");
					object fieldValue = S1Reflection.GetFieldValue(obj, text2);
					if (fieldValue == null)
					{
						stringBuilder.Append("null");
					}
					else
					{
						stringBuilder.Append(fieldValue.ToString());
					}
					stringBuilder.Append(Environment.NewLine);
				}
				this.Verbose(stringBuilder.ToString());
			}
		}
		public void Verbose(string message)
		{
			this.Verbose(message, new object[0]);
		}
		public void Verbose(string message, params object[] arg)
		{
			this.WriteLog((arg.Length > 0) ? string.Format(message, arg) : message, S1LogLevel.Verbose);
		}
		public void Log(string message)
		{
			this.WriteLog(message, S1LogLevel.Log);
		}
		public void Log(string message, params object[] arg)
		{
			this.WriteLog((arg.Length > 0) ? string.Format(message, arg) : message, S1LogLevel.Log);
		}
		public void Error(string message)
		{
			this.Error(message, new object[0]);
		}
		public void Error(string message, params object[] arg)
		{
			this.WriteLog((arg.Length > 0) ? string.Format(message, arg) : message, S1LogLevel.Error);
		}
		public void Exception(Exception ex)
		{
			this.WriteLog(ex.ToString(), S1LogLevel.Exception);
		}
		public void StartMeasure(string key)
		{
			this.AddMeasure(key, true);
		}
		public void EndMeasure(string key)
		{
			this.AddMeasure(key, false);
		}
		private void AddMeasure(string key, bool start)
		{
			if (this.verboseConfig.UseS1Trace && HttpContext.Current != null)
			{
				object obj = HttpContext.Current.Items["SOTRACE_MEASURES"];
				List<S1MeasurementItem> list;
				if (obj != null && obj is List<S1MeasurementItem>)
				{
					list = (List<S1MeasurementItem>)obj;
				}
				else
				{
					list = new List<S1MeasurementItem>();
					HttpContext.Current.Items["SOTRACE_MEASURES"] = list;
				}
				list.Add(new S1MeasurementItem
				{
					Key = key.ToUpper(),
					Start = start,
					Timestamp = S1Timestamp.GetTimestamp()
				});
			}
		}
		public static void SetCustomLogger(S1LogDelegate logger)
		{
			S1Log._defaultCustomLogger = logger;
		}
		public static void SetRedirector(S1LogDelegate logger)
		{
			S1Config.S1Config.AddOptionalFile("Log.s1config");
			S1Log._redirectLogger = logger;
		}
    }
}
